Skip to content

Conversation

Tristan-Wilson
Copy link
Member

This change adds support for the new beacon chain endpoint /eth/v1/beacon/blobs/{block_id} introduced in Fusaka while maintaining backward compatibility with the legacy endpoint
/eth/v1/beacon/blob_sidecars/{slot}. block_id can be a slot so Nitro just uses slot.

The new endpoint supports server-side filtering by versioned hash via query parameters. Since the Arbitrum sequencer inbox message contains the versioned hashes of the blobs that were posted, we can include those in the query.

Key changes:

  • Added UseLegacyEndpoint flag to BlobClientConfig to control which endpoint to use
  • Created new GetBlobsBySlot() public method for direct slot-based blob fetching
  • Implemented getBlobs() method for the new endpoint with versioned hash verification
  • Updated beaconRequest() to support query parameters for filtering
  • Added KZG commitment verification when versioned hashes are provided

Created blobtool CLI utility for testing both endpoints:

 # Fetch specific blob using new endpoint (default)
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash>

 # Fetch using legacy endpoint (requires versioned hashes)
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash> --use-legacy-endpoint

 # Compare both endpoints side-by-side
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash> --compare-endpoints

The new endpoint is used by default, with automatic fallback behavior maintained through the existing secondary beacon URL mechanism.

Spec reference: https://github.com/ethereum/beacon-APIs/blob/master/apis/beacon/blobs/blobs.yaml
NIT-3885

Cherry pick of #3826 and #3830

Testing

To test the new fetching method I added a new tool called blobtool. It's intended to be used for testing beacon endpoints, and it can use either the new or old method for fetching blobs. This is even more important after Fusaka where it is even more difficult to configure beacon nodes to have and keep all blobs. In future we may want to add some features like looking up blobs by block number, which is something operators often want to do.

$ target/bin/blobtool fetch --beacon-url=https://sepolia --slot 8726798                              

Fetching all blobs for slot 8726798 using new blobs endpoint...
Successfully fetched 4 blobs in 550.156533ms
Blob 0: versioned_hash=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf, size=131072 bytes
Blob 1: versioned_hash=0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a, size=131072 bytes
Blob 2: versioned_hash=0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc, size=131072 bytes
Blob 3: versioned_hash=0x012873697f6a3baa3ed5b0e53784a2b389fee4c341253a5ef69a85bd720d2aad, size=131072 bytes

$ target/bin/blobtool fetch --beacon-url=https://sepolia --slot 8726798 --versioned-hashes=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf,0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a,0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc                               
Fetching 3 blobs for slot 8726798 using new blobs endpoint...
Successfully fetched 3 blobs in 3.075861523s
Blob 0: versioned_hash=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf (computed=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf), size=131072 bytes
Blob 1: versioned_hash=0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a (computed=0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a), size=131072 bytes
Blob 2: versioned_hash=0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc (computed=0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc), size=131072 bytes

$ target/bin/blobtool fetch --beacon-url=https://sepolia --slot 8726798 --versioned-hashes=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf,0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a,0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc --use-legacy-endpoint                             
Fetching 3 blobs for slot 8726798 using legacy blob_sidecars endpoint...
Successfully fetched 3 blobs in 3.254657814s
Blob 0: versioned_hash=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf (computed=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf), size=131072 bytes
Blob 1: versioned_hash=0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a (computed=0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a), size=131072 bytes
Blob 2: versioned_hash=0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc (computed=0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc), size=131072 bytes

$ blobtool fetch --beacon-url=https://sepolia --versioned-hashes=0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf,0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a,0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc --compare-endpoints
Comparing legacy blob_sidecars and new blobs endpoints...

Fetching with legacy blob_sidecars endpoint...
✓ Legacy endpoint: fetched 3 blobs in 2.242901587s

Fetching with new blobs endpoint...
✓ New endpoint: fetched 3 blobs in 498.302806ms

Comparing blob data...
  Blob 0: ✓ identical (0x01ac7e2c7fff656a02bb7d8e9934798ab3d1df8fbf20acff5aec87b183fd99cf)
  Blob 1: ✓ identical (0x01808fa4fb38311c3a2a7c8a37d95af5a18eb3ccfe279d7e02f5df7d839b4a3a)
  Blob 2: ✓ identical (0x01e0b7530de207b244ce87448a633d7bb077339236d4a4e82985b944553913dc)

Performance comparison:
  Legacy endpoint: 2.242901587s
  New endpoint:    498.302806ms
  New endpoint is 77.8% faster

This change adds support for the new beacon chain endpoint
`/eth/v1/beacon/blobs/{block_id}` introduced in Fusaka while maintaining
backward compatibility with the legacy endpoint
`/eth/v1/beacon/blob_sidecars/{slot}`. block_id can be a slot so Nitro
just uses slot.

The new endpoint supports server-side filtering by versioned hash via
query parameters. Since the Arbitrum sequencer inbox message contains
the versioned hashes of the blobs that were posted, we can include those
in the query.

Key changes:
- Added `UseLegacyEndpoint` flag to BlobClientConfig to control which
  endpoint to use
- Created new `GetBlobsBySlot()` public method for direct slot-based
  blob fetching
- Implemented `getBlobs()` method for the new endpoint with versioned
  hash verification
- Updated `beaconRequest()` to support query parameters for filtering
- Added KZG commitment verification when versioned hashes are provided

Created `blobtool` CLI utility for testing both endpoints:
```
 # Fetch specific blob using new endpoint (default)
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash>

 # Fetch using legacy endpoint (requires versioned hashes)
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash> --use-legacy-endpoint

 # Compare both endpoints side-by-side
 blobtool fetch --beacon-url=<url> --slot=<slot> --versioned-hashes=<hash> --compare-endpoints
```

The new endpoint is used by default, with automatic fallback behavior
maintained through the existing secondary beacon URL mechanism.

Spec reference: https://github.com/ethereum/beacon-APIs/blob/master/apis/beacon/blobs/blobs.yaml
Copy link

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
2186 4 2182 0
View the top 3 failed tests by shortest run time
TestVersion30
Stack Traces | 5.780s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
=== PAUSE TestVersion30
=== CONT  TestVersion30
    precompile_inclusion_test.go:90: goroutine 522004 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.3/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x4081210, 0xc002adae00}, {0x403f460, 0xc0fa7e0ea0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc002adae00, {0x403f460, 0xc0fa7e0ea0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:1723 +0x5d
        github.com/offchainlabs/nitro/system_tests.testPrecompiles(0xc002adae00, 0x1e, {0xc2cf947db0, 0x6, 0x0?})
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:90 +0x371
        github.com/offchainlabs/nitro/system_tests.TestVersion30(0xc002adae00?)
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:67 +0x798
        testing.tRunner(0xc002adae00, 0x3cc98c0)
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1997 +0x465
        
    precompile_inclusion_test.go:90: �[31;1m [] execution aborted (timeout = 5s) �[0;0m
--- FAIL: TestVersion30 (5.78s)
TestVersion40
Stack Traces | 5.820s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [10-14|17:56:51.925] HTTP server stopped                      endpoint=127.0.0.1:37353
TRACE[10-14|17:56:51.925] P2P networking is spinning down
    precompile_inclusion_test.go:90: goroutine 522005 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.3/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x4081210, 0xc002adafc0}, {0x403f460, 0xc0b779ab10}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc002adafc0, {0x403f460, 0xc0b779ab10}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:1723 +0x5d
        github.com/offchainlabs/nitro/system_tests.testPrecompiles(0xc002adafc0, 0x28, {0xc064835df8, 0x5, 0x39?})
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:90 +0x371
        github.com/offchainlabs/nitro/system_tests.TestVersion40(0xc002adafc0?)
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:71 +0x64b
        testing.tRunner(0xc002adafc0, 0x3cc98c8)
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1997 +0x465
        
    precompile_inclusion_test.go:90: �[31;1m [] execution aborted (timeout = 5s) �[0;0m
--- FAIL: TestVersion40 (5.82s)
TestArbOSVersion50
Stack Traces | 6.230s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
=== PAUSE TestArbOSVersion50
=== CONT  TestArbOSVersion50
    precompile_inclusion_test.go:90: goroutine 522006 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.3/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x4081210, 0xc002adb180}, {0x403f460, 0xc142b129f0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc002adb180, {0x403f460, 0xc142b129f0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:1723 +0x5d
        github.com/offchainlabs/nitro/system_tests.testPrecompiles(0xc002adb180, 0x32, {0xc08a74be88, 0x3, 0x5be7b20?})
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:90 +0x371
        github.com/offchainlabs/nitro/system_tests.TestArbOSVersion50(0xc002adb180?)
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:75 +0x3ef
        testing.tRunner(0xc002adb180, 0x3cc87a8)
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1997 +0x465
        
    precompile_inclusion_test.go:90: �[31;1m [] execution aborted (timeout = 5s) �[0;0m
--- FAIL: TestArbOSVersion50 (6.23s)

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Copy link
Member

@joshuacolvin0 joshuacolvin0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@joshuacolvin0 joshuacolvin0 merged commit 899f8da into v3.8.x-release Oct 14, 2025
17 checks passed
@joshuacolvin0 joshuacolvin0 deleted the v3.8.x-release-support-beacon-blob-api branch October 14, 2025 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants